/*
 * SessionHolder.java
 *
 */

package cn.com.sbell.vss.util;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import cn.com.sbell.vss.Util;
import cn.com.sbell.vss.VissSession;
import cn.com.sbell.vss.VssException;

/**
 * 
 * @author Yifu.HUANG
 */
public class SessionHolder {
	private static final Log log = LogFactory.getLog(SessionHolder.class);

	private static final Map sessionMap = Collections
			.synchronizedMap(new LinkedHashMap());

	private long sessionTimeout = 600000L;

	private volatile long lastCleanTime = System.currentTimeMillis();

	public void putSession(VissSession session) {
		if (session == null) {
			return;
		}
		cleanTimeoutSession();
		session.setLastAccessTime(System.currentTimeMillis());
		sessionMap.put(session.getSessionId(), session);
		if (log.isDebugEnabled()) {
			log.debug("Put session: " + session.getSessionId());
		}
	}

	public int getSessionSize(String organizationId) {
		if (organizationId == null || "".equals(organizationId.trim())) {
			return sessionMap.size();
		}
		List sessions = getAllSessions();
		int size = 0;
		if (sessions != null && !sessions.isEmpty()) {
			for (int i = 0; i < sessions.size(); i++) {
				VissSession session = (VissSession) sessions.get(i);
				if (organizationId.equals(session.getOrganizationId()) && Util.strIsNull(session.getVapPath())) {
					size++;
				}
			}
		}
		return size;
	}

	public VissSession getSession(String sessionId) {
		if (sessionId == null) {
			return null;
		}
		VissSession session = (VissSession) sessionMap.get(sessionId);
		if (session == null) {
			return null;
		}
		long currentTimeMillis = System.currentTimeMillis();
		if (session.getLastAccessTime() + sessionTimeout < currentTimeMillis) {
			return null;
		}
		// update last access time
		session.setLastAccessTime(currentTimeMillis);
		synchronized (sessionMap) {
			Object obj = sessionMap.remove(sessionId);
			if (obj != null) {
				sessionMap.put(sessionId, obj);
			}
		}
		return session;
	}

	public List findSessionByUserId(String userId) {
		if (userId == null) {
			return null;
		}
		long timeoutTimeMillis = System.currentTimeMillis() - sessionTimeout;

		List list = new ArrayList();
		// int size = sessionMap.size();
		Object[] sessions = null;

		synchronized (sessionMap) {
			sessions = sessionMap.values().toArray();
		}

		for (int i = 0; i < sessions.length; i++) {
			VissSession session = (VissSession) sessions[i];
			if (session == null) {
				break;
			}
			if (session.getLastAccessTime() < timeoutTimeMillis) {
				// session timeout
				sessionMap.remove(session.getSessionId());
			}
			if (userId.equals(session.getUserId())) {
				list.add(session);
			}
		}
		return list;
	}

	public List getAllSessions() {
		return new LinkedList(sessionMap.values());
	}

	public VissSession removeSession(String sessionId) {
		if (sessionId == null) {
			return null;
		}
		VissSession data = (VissSession) sessionMap.remove(sessionId);
//		invalidate interdomain routing
		
		if (data != null) {
			data.invalidate();
			if (log.isDebugEnabled()) {
				log.debug("Session (id = " + sessionId + ") was removed!");
			}
		}
		return data;
	}

	public void cleanTimeoutSession() {
		cleanTimeoutSession(sessionTimeout);
	}

	public void cleanTimeoutSession(long timeout) {
		long currentTimeMillis = System.currentTimeMillis();
		if ((lastCleanTime + timeout / 2) > currentTimeMillis) {
			// dont need clean
			return;
		} else {
			lastCleanTime = currentTimeMillis;
		}
		Object[] sessions = null;
		synchronized (sessionMap) {
			sessions = sessionMap.values().toArray();
		}

		long timeoutTimeMillis = currentTimeMillis - timeout;
		boolean finished = false;
		for (int i = 0; i < sessions.length; i++) {
			// Set keys = sessionMap.keySet();
			VissSession session = (VissSession) sessions[i];
			if (session == null) {
				break;
			}
			if (session.getLastAccessTime() < timeoutTimeMillis) {
				sessionMap.remove(session.getSessionId());
				if (log.isDebugEnabled()) {
					if (finished) {
						throw new VssException("1234567890",
								"clean operation is finished.");
					}
					log.debug("Cleaned session: " + session);
				}
			} else {
				// use linked hash map
				if (log.isDebugEnabled()) {
					finished = true;
				} else {
					break;
				}
			}
		}
	}

	public long getSessionTimeout() {
		return sessionTimeout;
	}

	public void setSessionTimeout(long sessionTimeout) {
		this.sessionTimeout = sessionTimeout;
	}

	public static void main(String[] args) {
		Map m = new LinkedHashMap();
		m.put("1", "1");
		m.put("2", "2");
		m.remove("1");
		m.put("1", "1");
		Object[] obj = m.values().toArray();
		for (int i = 0; i < obj.length; i++) {
			System.out.println(obj[i]);
		}

	}
}
